#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define SOCKOPT_1 SO_REUSEADDR //SO_REUSEADDR|SO_REUSEPORT
char* HELLOWORD="hello\n";
char* remoteip="127.0.0.1";
int remoteport=0;
int localport=12345;

static struct sockaddr_in lastpeer;
static char grecvbuf[257];
#define ERR_EXIT(m) \
    do \
{ \
    perror(m); \
    exit(EXIT_FAILURE); \
    } while(0)
static void SetLocalPort(int port){
	localport=port;
}
static int GetLocalPort(){
	return localport;
}
static void SetRemoteIP(char* str){
	remoteip=str;
}
static char* GetRemoteIP(){
	return remoteip;
}
static void SetRemotePort(int port){
	remoteport=port;
}
static int GetRemotePort(){
	return remoteport;
}
static char* GetRecvBuff(){
	return grecvbuf;
}
static int GetRecvMax(){
	return sizeof(grecvbuf)-1;
}
static int GetTime(int t0){
	int t=time(NULL);
	return t-t0;
}
void printhex(unsigned char* buffer,int len){
	while(len>16){
		printhex(buffer,16);
		buffer+=16;
		len-=16;
	}
	for(int i=0;i<len;i++){
		fprintf(stderr,"%02x ",(unsigned char)buffer[i]);
	}
	for(int i=0;i<16-len;i++){
		fprintf(stderr,"   ");
	}
	for(int i=0;i<len;i++){
		switch(buffer[i]){
			case 0x00:
			case 0x09:
			case 0x0a:
			case 0x0d:
				fputc('.',stderr);
				break;
			default:
				fputc(buffer[i],stderr);
		}
	}
	fprintf(stderr,"\n");
}

static void print_recv(unsigned char* buffer,int len,struct sockaddr_in *peeraddr){
	fprintf(stderr,"从%s:%d接收到数据:\n",inet_ntoa(peeraddr->sin_addr),ntohs(peeraddr->sin_port));
	printhex(buffer,len);
	fprintf(stderr,"-------------\n");
}
int recv_1(int sock){
	struct sockaddr_in peeraddr;
	socklen_t peerlen;
	char *recvbuf;
	int maxlen;
	int n;
	peerlen = sizeof(peeraddr);
	recvbuf=GetRecvBuff();
	maxlen=GetRecvMax();
	n = recvfrom(sock, recvbuf, maxlen, MSG_DONTWAIT|MSG_PEEK,(struct sockaddr *)&peeraddr, &peerlen);
	if(n>0){
		recvbuf[n]=0;
		print_recv(recvbuf,n,&peeraddr);
		return n;
	}
	return 0;
}

void shakehand(){
	int port;
	int sock1,sock2;
	int retry=5;
	port=GetRemotePort();
	if(port){
		int sock;
		struct sockaddr_in servaddr;
		time_t t;
		char temp[64];
		fprintf(stderr,"shakehand port:%d\n",port);
		memset(&servaddr, 0, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		servaddr.sin_port = htons(port);
		servaddr.sin_addr.s_addr = inet_addr(GetRemoteIP());
		sock1=opensock(localport);
		sock2=opensock(0);
		while(retry-->0){
			if(recv_1(sock1)>0){
				printf("ok\n");
				return;
			}else{
				sendto(sock2, HELLOWORD, strlen(HELLOWORD), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
				sleep(1);
			}
		}
		printf("error\n");
	}
}
int opensock(int port){
	int sock;
	struct sockaddr_in servaddr;
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(GetLocalPort());
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0){
		ERR_EXIT("socket");
	}
	if(port){
		int on=1;
		if(setsockopt(sock,SOL_SOCKET,SOCKOPT_1,&on,sizeof(int))<0){
			ERR_EXIT("setsockopt error");
		}
		if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
			ERR_EXIT("bind error");
		}
	}
	return sock;
}
void closesock(int sock){
	close(sock);
}

//serverport clientport
int main(int argc,char* argv[])
{
	int sock;
	if(argc>=2){
		char* temp=argv[1];
		int port;
		SetRemoteIP(strsep(&temp,":"));
		sscanf(temp,"%d",&port);
		SetRemotePort(port);
	}
	if(argc>=3){
		int port;
		sscanf(argv[2],"%d",&port);
		SetLocalPort(port);
	}
	shakehand();
	
	return 0;
}
